
#include "debug.h"

#ifdef DEBUG

#include <stdexcept>


Debug::Debug()
{
	exception e;
	debugFile = fopen("debug.log", "w+t");
	if(debugFile == NULL)
	{
		throw exception("Can't open debug output file.");
	}
}


Debug::~Debug()
{
	if(debugFile != NULL)
	{
		fclose(debugFile);
	}
}


void Debug::ReportLiterally(char * str)
{
	if(fprintf(debugFile, str) < 0)
	{
		throw exception("Can't write debug output.");
	}
}


void Debug::ReportLine(char * str)
{
	if(fprintf(debugFile, str) < 0)
	{
		throw exception("Can't write debug output.");
	}
	if(fprintf(debugFile, "\n") < 0)
	{
		throw exception("Can't write debug output.");
	}
}


void Debug::PrintLoadedFiles(GenericTable<LoadedFile> * loadedFiles)
{
	ReportLine("Making output of files:");

	for(int i = 0; i < loadedFiles->GetCount(); i++)
	{
		LoadedFile * lf = loadedFiles->GetData(i);

		ReportLiterally("  File: ");
//		ReportLine(lf->fileName);

		ReportLine("  Printing file content:");

		// TODO: make it better
		char tempArray[16];

		while(1)
		{
			int c = -1;
			try
			{
				c = lf->GetNextCharacter();
			}
			catch(...)
			{
				break;
			}

			if(c == -1)
			{
				break;
			}

			sprintf(tempArray, "%c", (char)c);
			ReportLiterally(tempArray);
		}
		/*
		for(int j = 0; j < lf->sourceLines->GetCount(); j++)
		{
			SourceLine * sl = lf->sourceLines->GetData(j);
			ReportLiterally("    ");
			ReportLine(sl->lineCharacters);
		}*/

		lf->RewindToBeginning();

		ReportLine("  End of file content.");
	}

	ReportLine("End of files output.");
}


void Debug::OutputLexicToXML(LexicUnits * lu)
{
	FILE * fo = fopen("debug_lexic.xml", "w+t");
	if(fo == NULL)
	{
		return;
	}

	// print xml header
	fprintf(fo, "<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n");
	fprintf(fo, "<lexic_analysis>\n");

    // perform output of lexic units
	fprintf(fo, "  <lexic_units>\n");

	// print unit by unit
	for(int i = 0; i < lu->uniformCharacters->GetCount(); i++)
	{
		PrintLexicUnitToXML(fo, lu->uniformCharacters->GetData(i), lu);
	}

	fprintf(fo, "  </lexic_units>\n");

	// print xml footer
	fprintf(fo, "</lexic_analysis>\n");

	fclose(fo);
}

void Debug::PrintLexicUnitToXML(FILE * fo, LexicUnit * lu, LexicUnits * lus)
{
	char enumToString[128]; // TODO (change 128)

	// unit header, id, source file id, source line, source column
	fprintf(fo, "    <unit id=\"%d\" file=\"%d\" line=\"%d\" column=\"%d\">\n", lu->id ,lu->sourceFileId ,lu->sourceLineId ,lu->sourceColumnId);
	
	// output type and subtype
	MakeStringOfLexicUnitType(enumToString, lu->type);
	fprintf(fo, "      <type>%s</type>\n", enumToString);
	fprintf(fo, "      <subtype id=\"%d\">%s</subtype>\n", lu->subtype, CodeForXML(lus->GetLexicUnitString(lu).c_str()).c_str());

	// unit footer
	fprintf(fo, "    </unit>\n");
}

void Debug::MakeStringOfLexicUnitType(char * str, LexicUnitType lut)
{
	switch(lut)
	{
		case LUTypeConstantBool: strcpy(str, "constant (boolean)"); break;
		case LUTypeConstantFloat: strcpy(str, "constant (float)"); break;
		case LUTypeConstantInteger: strcpy(str, "constant (integer)"); break;
		case LUTypeConstantString: strcpy(str, "constant (string)"); break;
		case LUTypeIdentifier: strcpy(str, "identifier"); break;
		case LUTypeKeyword: strcpy(str, "keyword"); break;
		case LUTypeOperator: strcpy(str, "operator"); break;
		case LUTypeSpecialCharacter: strcpy(str, "special character"); break;
		case LUTypeSpecialType: strcpy(str, "special type"); break;
		default: strcpy(str, "");
	}
}


void Debug::OutputSyntaxToXML(AbstractSyntaxTree * ast, LexicUnits * lus)
{
	FILE * fo = fopen("debug_syntax.xml", "w+t");
	if(fo == NULL)
	{
		return;
	}

	// print xml header
	fprintf(fo, "<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n");
	fprintf(fo, "<syntax_analysis>\n");

    // perform output of syntax tree
	fprintf(fo, "  <syntax_tree>\n");

	// print root node
	PrintSyntaxTreeNodeToXML(fo, ast->RootNode, 4, lus);

	fprintf(fo, "  </syntax_tree>\n");

	// print xml footer
	fprintf(fo, "</syntax_analysis>\n");

	fclose(fo);
}

void Debug::PrintSyntaxTreeNodeToXML(FILE * fo, SyntaxTreeNode * node, int indent, LexicUnits * lus)
{
	string nodeParameters;
	// prepare to print data
	if(node->LexUnitID == -1)
	{
		// production
		nodeParameters.append("production=\"");
		nodeParameters.append(CodeForXML(AbstractSyntaxTree::ConvertProductionToString(node->Production)));
		nodeParameters.append("\"");
	}
	else
	{
		char number[128]; // TODO - remove 128

		// normal unit
		LexicUnit lu = lus->GetLexicUnit(node->LexUnitID);
		nodeParameters.append("id=\"");
		sprintf(number, "%d", lu.id);
		nodeParameters.append(number);
		nodeParameters.append("\" content=\"");
		nodeParameters.append(CodeForXML(lus->GetLexicUnitString(&lu).c_str()));
		nodeParameters.append("\"");
	}

	// print node header
	PrintSpaces(fo, indent); fprintf(fo, "<node %s>\n", nodeParameters.c_str());
	
    
	// print subnodes
	for(int i = 0; i < node->Nodes->GetCount(); i++)
	{
		PrintSyntaxTreeNodeToXML(fo, node->Nodes->GetData(i), indent+2, lus);
	}

	// print node footer
	PrintSpaces(fo, indent); fprintf(fo, "</node>\n");
}

void Debug::PrintSpaces(FILE * fo, int count)
{
	for(int i = 0; i < count; i++)
	{
		fprintf(fo, " ");
	}
}

string Debug::CodeForXML(const char * s)
{
	string retVal("");
	char tempArray[2];
	tempArray[1] = '\0';

	while(*s)
	{
		switch(*s)
		{
			case '<': retVal.append("&lt;"); break;
			case '>': retVal.append("&gt;"); break;
			case '&': retVal.append("&amp;"); break;
			default: tempArray[0] = *s; retVal.append(tempArray);
		}
		s++;
	}

	return retVal;
}


void Debug::OutputSemanticsStructuresToXML(SemanticAnalyzer * sa, LexicUnits * lus)
{
	FILE * fo = fopen("debug_semantic_structures.xml", "w+t");
	if(fo == NULL)
	{
		return;
	}

	// print xml header
	fprintf(fo, "<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n");
	fprintf(fo, "<semantic_structures>\n");


	// make complete output - complete semantic analyzer
	sa->DebugXMLOutput(fo, lus);


	// print xml footer
	fprintf(fo, "</semantic_structures>\n");

	fclose(fo);

}


void Debug::OutputSemanticsStructuresFlattenToXML(SemanticAnalyzer * sa, LexicUnits * lus)
{
	FILE * fo = fopen("debug_semantic_structures_flatten.xml", "w+t");
	if(fo == NULL)
	{
		return;
	}

	// print xml header
	fprintf(fo, "<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n");
	fprintf(fo, "<semantic_structures>\n");


	// make complete output - complete semantic analyzer, but flatten data
	sa->DebugXMLOutputFlatten(fo, lus);


	// print xml footer
	fprintf(fo, "</semantic_structures>\n");

	fclose(fo);

}


#endif


